#include "i386/kernel/i8259.h"

#include "linux/stddef.h"

#include "i386/kernel/trap.h"
#include "i386/kernel/io.h"
#include "i386/kernel/screen.h"

#include "common/linkage.h"
#include "common/basictypes.h"

#include "linux/irq.h"
#include "linux/irq_vector.h"


u32 cached_irq_mask = 0xffff;

void (*interrupt[NR_IRQS])(void);

irq_desc_t irq_desc[NR_IRQS];

static void enable_8259A_irq(u32 irq) {
  u32 mask = ~(1 << irq);
  cached_irq_mask &= mask;
  if (irq & 8) {
    outb(cached_slave_mask, PIC_SLAVE_IMR);
  } else {
    outb(cached_master_mask, PIC_MASTER_IMR);
  }
}

static void disable_8259A_irq(u32 irq) {
  u32 mask = 1 << irq;
  cached_irq_mask |= mask;
  if (irq & 8) {
    outb(cached_slave_mask, PIC_SLAVE_IMR);
  } else {
    outb(cached_master_mask, PIC_MASTER_IMR);
  }
}

static void mask_and_ack_8259A(u32 irq) {
  puts("Not implement: mask_and_ack_8259A");
}

static void end_8259A_irq(u32 irq) {
  puts("Not implement: end_8259A_irq");
}

static u32 startup_8259A_irq(u32 irq) {
  enable_8259A_irq(irq);
  return irq;
}

static void shutdown_8259A_irq(u32 irq) {
  puts("Not implement: shutdown_8259A_irq");
}


static struct hw_interrupt_type i8259A_irq_type = {
  "XT-PIC",
  startup_8259A_irq,
  shutdown_8259A_irq,
  enable_8259A_irq,
  disable_8259A_irq,
  mask_and_ack_8259A,
  end_8259A_irq,
  // NULL,
};

/*
 * 8259A
 *
 */
void __init init_8259A(int eoi) {
  // 默认情况下， Linux是使用 eoi = 0 进行初始化的
  // 在这里我们没有实现eoi = 0 的代码
  outb(0xff, PIC_MASTER_IMR);   // master all of 8259A-1
  outb(0xff, PIC_SLAVE_IMR);    // master all of 8259A-2

  outb(0x11, PIC_MASTER_CMD);  /* ICW1 */
  outb(0x20 + 0, PIC_MASTER_IMR); /* ICW2, the index of first interrupt */
  outb(1U << PIC_CASCADE_IR, PIC_MASTER_IMR); /* ICW3 */
  outb(MASTER_ICW4_DEFAULT | PIC_ICW4_AEOI, PIC_MASTER_IMR);

  outb(0x11, PIC_SLAVE_CMD);
  outb(0x20 + 8, PIC_SLAVE_IMR);
  outb(PIC_CASCADE_IR, PIC_SLAVE_IMR);
  outb(SLAVE_ICW4_DEFAULT, PIC_SLAVE_IMR);
  io_delay();

  /*
   * 回复屏蔽位
   */
  outb(cached_master_mask, PIC_MASTER_IMR);
  outb(cached_slave_mask, PIC_SLAVE_IMR);
  
}

void __init init_ISA_irqs(void) {
  init_8259A(1);

  for (int i = 0; i < NR_IRQS; ++i) {
    irq_desc[i].status = IRQ_DISABLED;
    irq_desc[i].action = NULL;
    irq_desc[i].depth = 1;

    if (i < 16) {
      irq_desc[i].handler = &i8259A_irq_type;
    } else {
      irq_desc[i].handler = &no_irq_type;
    }
  }
}

/*
 *
 */
void __init init_IRQ(void) {
  /*
   * 此处本来是通过 pre_intr_init_hook 调用，以支持多种8259, APIC等
   */
  init_ISA_irqs();
  
  for (int i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); ++i) {
    int vector = FIRST_EXTERNAL_VECTOR + i;
    if (i >= NR_IRQS)
      break;
    if (vector != SYSCALL_VECTOR) {
      set_intr_gate(vector, (u32)(interrupt[i]));
    }
  }
}
